package sdk.old;

import java.util.ArrayList;

public class Field
{
	private Square[][] field = new Square[3][3];
	private String[][] fieldMerge = new String[9][9];

	private Square[][] hypo = new Square[9][9];

	public Field(Square[][] field)
	{
		super();
		this.field = field;
		merge();
		inizializeHypo();
	}

	public Field(String[][] pattern)
	{
		fieldMerge = pattern;
		Square[][] field = new Square[3][3];
		for (int r = 0; r < 9; r++)
		{
			for (int c = 0; c < 9; c++)
			{
				int rs = r / 3;
				int cs = c / 3;
				field[rs][cs].set(r % 3, c % 3, fieldMerge[r][3]);
			}
		}
		inizializeHypo();
	}

	public Field(String pattern) throws Exception
	{
		String[][] fieldMerge = new String[9][9];
		String[] s = pattern.split(",");
		for (int k = 0; k < s.length; k++)
		{
			int r = k / 9;
			int c = k % 9;
			fieldMerge[r][c] = s[k];

		}
		this.fieldMerge = fieldMerge;

		Square[][] field = new Square[3][3];

		for (int r = 0; r < 3; r++)
		{
			for (int c = 0; c < 3; c++)
			{
				field[r][c] = new Square();
			}

		}
		this.field = field;
		for (int r = 0; r < 9; r++)
		{
			for (int c = 0; c < 9; c++)
			{
				int rf = r / 3;
				int cf = c / 3;

				int rs = r % 3;
				int cs = c % 3;
				field[rf][cf].set(rs, cs, fieldMerge[r][c]);
			}
		}
		inizializeHypo();
	}

	private void inizializeHypo()
	{
		for (int r = 0; r < 9; r++)
		{
			for (int c = 0; c < 9; c++)
			{
				hypo[r][c] = new Square();
			}
		}
		System.out.println(hypo);
	}

	private void merge()
	{
		for (int rSquare = 0; rSquare < 3; rSquare++)
		{
			for (int cSquare = 0; cSquare < 3; cSquare++)
			{
				Square q = field[rSquare][cSquare];
				for (int r = 0; r < 3; r++)
				{
					for (int c = 0; c < 3; c++)
					{
						int rr = r + rSquare * 3;
						int cc = c + cSquare * 3;
						fieldMerge[rr][cc] = q.get(r, c);
					}

				}
			}

		}

	}

	public Square[][] getField()
	{
		return field;
	}

	public void setField(Square[][] field)
	{
		this.field = field;
		merge();
	}

	public boolean validate()
	{
		//test Rows
		for(int r=0;r<9;r++){
			String test  =getRow(r);
			if(!testSerie(test)){
				System.out.println("row "+r+" incorrect");
				return false;
			}
		}
		
		//test Cols
		for(int c=0;c<9;c++){
			String test  =getCol(c);
			if(!testSerie(test)){
				System.out.println("col "+c+" incorrect");
				return false;
			}
		}
		
		//test Square
		for(int r =0;r<2;r++){
			for(int c=0;c<2;c++){
				Square s =field[r][c];
				if(!s.validate()){
					System.out.println("square ["+r+","+c+"] incorrect");
					return false;
				}
			}
		}
		
		return true;
	}

	private boolean testSerie(String test)
	{
		
		for(int i=1;i<10;i++)
		{	
			test=test.replace(i+"", "");
		}
		if(test.length()==0)
			return true;
		return false;
	}

	public void print()
	{
		printMerged();
	}

	public String getRow(int rSqare)
	{
		String res = "";
		for (int c = 0; c < 9; c++)
		{
			res = res + fieldMerge[rSqare][c];
		}
		res = sortString(res);
		return res;
	}

	private String sortString(String res)
	{
		String sorted = "";
		for (int i = 1; i < 10; i++)
		{
			String d = i + "";
			if (!res.contains(i + ""))
				d = "0";
			sorted = sorted + d;
		}
		return sorted;
	}

	public String getCol(int cSquare)
	{
		String res = "";
		for (int r = 0; r < 9; r++)
		{
			res = res + fieldMerge[r][cSquare];
		}
		res = sortString(res);
		return res;
	}

	public void printMerged()
	{

		String str = "";
		str = str + "+-------+-------+-------+\n";
		for (int r = 0; r < 9; r++)
		{
			str = str + "| ";
			for (int c = 0; c < 9; c++)
			{
				if (!fieldMerge[r][c].equalsIgnoreCase("0"))
				{
					str = str + fieldMerge[r][c] + " ";
				}
				else
				{

					str = str + "  ";
				}
				if (c % 3 == 2 && c != 0)
				{
					str = str + "| ";
				}
			}
			str = str + "\n";
			if (r % 3 == 2 && r != 0)
			{
				str = str + "+-------+-------+-------+\n";
			}

		}
		System.out.println(str);

	}

	public boolean validateRow(int r)
	{
		String str = getRow(r);
		return validateString(str);
	}

	public boolean validateCol(int c)
	{
		String str = getCol(c);
		return validateString(str);
	}

	private boolean validateString(String str)
	{
		String total = "";
		for (int c = 0; c < 9; c++)
		{
			String d = str.substring(c, c + 1);
			if (d.length() != 0 && total.contains(d))
				return false;
			if (!"0".equalsIgnoreCase(d))
				total = total + d;
		}
		return true;
	}

	public boolean validateSquare(int r, int c)
	{
		return field[r][c].validate();
	}

	/**
	 * solve SDK Schema
	 */
	public void solve()
	{
		int step = 0;
		while (step < 1000)
		{
			this.print();
			System.out.println("STEP: " + step);
			int solved = solveHypo();
			System.out.println("Soled: " + solved);
			this.print();
			step++;
		}

	}

	private int solveHypo()
	{

		int solvedSingleton=0;
		int solvedSingletonRCS=0;
		int sol=0;
		do
		{
			makeHypo();
			sol = SolveSingleton();
			solvedSingleton=solvedSingleton+sol;
		}
		while (sol!=0);
		
		if (validate()){
			System.out.println("SOLVED");
			print();
			System.exit(0);
		}
		
		System.out.println("Solved ("+solvedSingleton+") Singleton");
		print();
		
		sol=0;
		do
		{
			makeHypo();
			sol = SolveSingletonRCS();
			solvedSingletonRCS=solvedSingletonRCS+sol;
		}
		while (sol!=0);
		
		if (validate()){
			System.out.println("SOLVED");
			print();
			System.exit(0);
		}
		
		
//		int solved2 = SolveSingletonInSquare();
//		makeHypo();
//		int solved3 = solveSingletonInLine();
//		makeHypo();
//		int solved4 = solveSingletonInRow();
//		makeHypo();
		return solvedSingleton ;//+ solved2+solved3+solved4;
	}

	//Solve Singleton in Row Cols and Square
	private int SolveSingletonRCS()
	{
		int solved2 = SolveSingletonInSquare();
//		makeHypo();
//		int solved3 = solveSingletonInLine();
//		makeHypo();
//		int solved4 = solveSingletonInRow();
		return solved2;//+solved3+solved4;
	}

	private int solveSingletonInRow()
	{
		// TODO Auto-generated method stub
		return 0;
	}

	private int solveSingletonInLine()
	{
		// TODO Auto-generated method stub
		return 0;
	}

	/**
	 * resolve Singleton in each 3x3 Square
	 * 
	 * @return
	 */
	private int SolveSingletonInSquare()
	{
		int solved = 0;
		for (int rExt = 0; rExt < 3; rExt++)
		{
			for (int cExt = 0; cExt < 3; cExt++)
			{
				String[] lines = new String[9];
				int row = 0;
				printHypo();
				for (int r = 0; r < 3; r++)
				{
					for (int c = 0; c < 3; c++)
					{
						int rowH = r + rExt * 3;
						int colH = c + cExt * 3;
						Square sq = hypo[rowH][colH];
						//System.out.println(sq.print());
						String d = sortString(sq.toLine());
						lines[row++] = (r + rExt * 3) + "," + (+c + cExt * 3) + "a" + d;
					}
				}
				ArrayList<String> singles = extractSingle(lines);
				if (singles != null && singles.size() > 0)
				{
					processSingles(singles);
					solved = solved + singles.size();
				}
			}
		}
		return solved;
	}

	/**
	 * process the Sigles found int square rows or cols
	 * 
	 * @param singles
	 */
	private void processSingles(ArrayList<String> singles)
	{
		for (String line : singles)
		{
			String[] pos = line.split("a")[0].split(",");
			String value = line.split("a")[1];
			int r =Integer.parseInt(pos[0]);
			int c =Integer.parseInt(pos[1]);
			alterField(r,c,value);
		}
	}

	/**
	 * put value in te row r and col c of the field anf field merged
	 * @param r
	 * @param c
	 * @param value
	 */
	private void alterField(int r, int c, String value)
	{
		//printMerged();
		fieldMerge[r][c] = value;
		//printMerged();
		int rc=r/3;
		int cc=c/3;
		Square q = field[rc][cc];
		//System.out.println(q.print());
		int rs=(r%3);
		int cs=(c%3);
		q.set(rs, cs, value);
		//System.out.println(q.print());
		
	}

	/**
	 * Extract, if exist single numbers from the array
	 * 
	 * @param lines
	 * @return
	 */
	private ArrayList<String> extractSingle(String[] lines)
	{
		ArrayList<String> res = new ArrayList<String>();
		int[] count = new int[9];
		for (int i = 0; i < 9; i++)
		{
			String line = lines[i];
			String number = line.split("a")[1];
			for (int j = 0; j < 9; j++)
			{
				if (!number.substring(j, j + 1).equalsIgnoreCase("0"))
					count[j]++;
			}
		}
		for (int i = 0; i < 9; i++)
		{
			if (count[i] == 1)
				res.add(lines[i].split("a")[0] + "a" + (i + 1));
		}

		// System.out.println(res);
		return res;
	}

	/**
	 * resolve the singleton hypoField
	 */
	private int SolveSingleton()
	{
		int solved = 0;
		for (int r = 0; r < 9; r++)
		{
			for (int c = 0; c < 9; c++)
			{
				Square sq = hypo[r][c];
				//System.out.println("hypo["+r+"]["+c+"] \n" + sq.print());
				String test = sq.toLine().replace("*", "");
				if (test.length() == 1)
				{
					alterField(r,c,test);
					solved++;
				}
			}
		}
		return solved;
	}

	/**
	 * Write in the cell r,c the val value and erase the r,c hypoField
	 * 
	 * @param r
	 * @param c
	 */
	private void completeField(int r, int c, String val)
	{
		fieldMerge[r][c] = val;
		Square h = new Square();
		hypo[r][c] = h;

	}

	/**
	 * if every hypoField is empty
	 * 
	 * @return
	 */
	private boolean isSolved()
	{
		for (int r = 0; r < 9; r++)
		{
			for (int c = 0; c < 9; c++)
			{
				Square sq = hypo[r][c];
				String test = sq.toLine().replace("*", "");
				if (test.length() > 0)
					return false;
			}

		}
		return true;
	}

	/**
	 * make Hypothesis on the 9x9 hypoField
	 */
	private void makeHypo()
	{
		for (int r = 0; r < 9; r++)
		{
			for (int c = 0; c < 9; c++)
			{
				hypo[r][c] = calcHypo(r, c);
			}
		}
		//printHypo();
	}

	/**
	 * make Hypothesis on the r,c hypoField
	 * 
	 * @param r
	 * @param c
	 * @return
	 */
	private Square calcHypo(int r, int c)
	{
		Square res = new Square();
		if (!fieldMerge[r][c].equalsIgnoreCase("0"))
			return res;
		String hypoRow = calcHypoRow(r);
		String hypoCol = calcHypoCol(c);
		String hypoSquare = calcHypoSquare(r, c);
		res = mergeHypo(hypoRow, hypoCol, hypoSquare);
		return res;
	}

	/**
	 * return the hypoField from row col and square single hypo
	 * 
	 * @param hypoRow
	 * @param hypoCol
	 * @param hypoSquare
	 * @return
	 */
	private Square mergeHypo(String hypoRow, String hypoCol, String hypoSquare)
	{
		Square res = new Square();
		for (int i = 0; i < 9; i++)
		{
			String rr = hypoRow.substring(i, i + 1);
			String cc = hypoCol.substring(i, i + 1);
			String sq = hypoSquare.substring(i, i + 1);
			String merge = rr + cc + sq;
			if (!merge.contains("0"))
			{
				int rsq = i / 3;
				int csq = i % 3;
				res.set(rsq, csq, rr);
			}

		}
		return res;
	}

	/**
	 * Calculate missing numbers in square r,s
	 * 
	 * @param r
	 * @param c
	 * @return
	 */
	private String calcHypoSquare(int r, int c)
	{
		String res = "";
		Square q = field[r / 3][c / 3];
		String square = q.toLine();

		square = sortString(square);
		res = getComplementaryString(square);

		// System.out.println("calcHypoSquare "+r+","+c);
		// System.out.println(" sqr:"+square);
		// System.out.println("-sqr:"+res);
		return res;
	}

	/**
	 * Calculate missing numbers in col c
	 * 
	 * @param c
	 * @return
	 */
	private String calcHypoCol(int c)
	{
		String res = "";
		String col = getCol(c);
		res = getComplementaryString(col);

		// System.out.println("calcHypoCol "+c);
		// System.out.println(" col:"+col);
		// System.out.println("-col:"+res);
		return res;
	}

	/**
	 * Calculate missing numbers in row r
	 * 
	 * @param r
	 * @return
	 */
	private String calcHypoRow(int r)
	{
		String res = "";
		String row = getRow(r);
		res = getComplementaryString(row);

		// System.out.println("calcHypoRow "+r);
		// System.out.println(" row:"+row);
		// System.out.println("-row:"+res);
		return res;
	}

	/**
	 * calculate missing numbers form existing numbers
	 * 
	 * @param string
	 * @return
	 */
	private String getComplementaryString(String string)
	{
		String res = "";
		for (int i = 1; i < 10; i++)
		{
			String d = i + "";
			if (string.contains(i + ""))
				d = "0";
			res = res + d;
		}
		return res;
	}

	public void printHypo()
	{

		//makeHypo();
		String [][] merge = new String [27][27];
		for (int r=0;r<9;r++){
			for (int c=0;c<9;c++){
				Square s = hypo[r][c];
				for (int rSq=0;rSq<3;rSq++){
					for (int cSq=0;cSq<3;cSq++){
						//System.out.println(s.get(rSq, cSq));
						merge[r*3+rSq][c*3+cSq]=s.get(rSq, cSq);
					}			
				}
			}			
		}
		
		String res="";
		res=res+"  ------ ------ ------ + ------ ------ ------ + ------ ------ ------  \n";
		for (int r=0;r<27;r++){
			res=res+"| ";
			for (int c=0;c<27;c++){
				res=res+merge[r][c]+" ";
				
				if(c!=0 && (c+1)%3==0)
					res=res+" ";
				if(c!=0 && (c+1)%9==0)
					res=res+"| ";
				
			}
			if(r!=0 && (r+1)%3==0)
				res=res+"\n";
			if(r!=0 && (r+1)%9==0)
				res=res+"  ------ ------ ------ + ------ ------ ------ + ------ ------ ------  ";
			res=res+"\n";
		}
		System.out.println(res);
	}

}
